/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.lvyh.lightframe.cloud.business.usercenter.service.impl;

import cn.hutool.core.util.StrUtil;
import com.github.pagehelper.Page;
import com.google.common.collect.Ordering;
import com.lvyh.lightframe.cloud.business.usercenter.bean.UcSysMenuBean;
import com.lvyh.lightframe.cloud.business.usercenter.bean.UcSysRoleBean;
import com.lvyh.lightframe.cloud.business.usercenter.bean.UcSysRoleMenuBean;
import com.lvyh.lightframe.cloud.business.usercenter.bean.UcSysRoleUserBean;
import com.lvyh.lightframe.cloud.business.usercenter.bean.UcSysUserBean;
import com.lvyh.lightframe.cloud.business.usercenter.service.SysUserService;
import com.lvyh.lightframe.cloud.business.usercenter.service.UserCenterMainDataService;
import com.lvyh.lightframe.cloud.common.constant.SystemConstants;
import com.lvyh.lightframe.cloud.common.enums.UserType;
import com.lvyh.lightframe.cloud.common.model.LoginAppUser;
import com.lvyh.lightframe.cloud.common.model.PageResult;
import com.lvyh.lightframe.cloud.common.model.Result;
import com.lvyh.lightframe.cloud.common.model.SysMenu;
import com.lvyh.lightframe.cloud.common.model.SysRole;
import com.lvyh.lightframe.cloud.common.model.SysUser;
import com.lvyh.lightframe.cloud.common.pagination.DefaultPageResult;
import com.lvyh.lightframe.cloud.common.pagination.PageParam;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

@Slf4j
@Service
public class SysUserServiceImpl implements SysUserService {

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    private UserCenterMainDataService userCenterMainDataService;

    @Override
    public SysUser getById(Integer id) throws Exception {
        UcSysUserBean sysUserBean = userCenterMainDataService.selectUcSysUserById(id);

        SysUser bean = new SysUser();
        BeanUtils.copyProperties(sysUserBean, bean);
        return bean;
    }

    @Override
    public boolean updateById(SysUser sysUser) throws Exception {
        UcSysUserBean condition = new UcSysUserBean();
        condition.setId(sysUser.getId());
        List<UcSysUserBean> sysUserList = userCenterMainDataService.selectUcSysUser(condition);
        if (!CollectionUtils.isEmpty(sysUserList)) {
            UcSysUserBean sysUserBean = sysUserList.get(0);
            BeanUtils.copyProperties(sysUser, sysUserBean);
            return userCenterMainDataService.updateUcSysUser(sysUserBean);
        }
        return false;
    }

    @Override
    public LoginAppUser findByUsername(String username) throws Exception {
        SysUser sysUser = this.selectByUsername(username);
        return getLoginAppUser(sysUser);
    }

    @Override
    public LoginAppUser findByOpenId(String username) throws Exception {
        SysUser sysUser = this.selectByOpenId(username);
        return getLoginAppUser(sysUser);
    }

    @Override
    public LoginAppUser findByMobile(String username) throws Exception {
        SysUser sysUser = this.selectByMobile(username);
        return getLoginAppUser(sysUser);
    }

    @Override
    public LoginAppUser getLoginAppUser(SysUser sysUser) throws Exception {
        if (sysUser != null) {
            LoginAppUser loginAppUser = new LoginAppUser();
            BeanUtils.copyProperties(sysUser, loginAppUser);

            List<SysRole> sysRoles = queryRolesByUserId(sysUser.getId());
            loginAppUser.setRoles(sysRoles);

            if (!CollectionUtils.isEmpty(sysRoles)) {
                List<Integer> roleIds = sysRoles.parallelStream().map(SysRole::getId).collect(Collectors.toList());
                List<SysMenu> menus = queryMenusByRoleIds(roleIds, SystemConstants.PERMISSION);
                if (!CollectionUtils.isEmpty(menus)) {
                    Set<String> permissions = menus.parallelStream().map(p -> p.getPath())
                            .collect(Collectors.toSet());
                    loginAppUser.setPermissions(permissions);
                }
            }
            return loginAppUser;
        }
        return null;
    }

    public List<SysMenu> queryMenusByRoleIds(List<Integer> roleIds, Integer type) throws Exception {
        UcSysRoleMenuBean condition = new UcSysRoleMenuBean();
        condition.setRoleIds(roleIds);
        List<UcSysRoleMenuBean> sysRoleMenuList = userCenterMainDataService.selectUcSysRoleMenu(condition);

        List<Integer> menuIds = new ArrayList<>();
        if (!CollectionUtils.isEmpty(sysRoleMenuList)) {
            for (UcSysRoleMenuBean sysRoleMenuBean : sysRoleMenuList) {
                menuIds.add(sysRoleMenuBean.getMenuId());
            }
        }

        List<UcSysMenuBean> sysMenuList = getSysMenuList(menuIds, null, type);
        sysMenuList.sort(Ordering.natural().onResultOf(UcSysMenuBean::getSort));

        List<SysMenu> result = convertMenu(sysMenuList);
        return result;
    }

    public List<SysMenu> convertMenu(List<UcSysMenuBean> sysMenuList) {
        List<SysMenu> result = new ArrayList<>();
        for (UcSysMenuBean bean : sysMenuList) {
            SysMenu sysMenu = new SysMenu();
            BeanUtils.copyProperties(bean, sysMenu);
            sysMenu.setHidden(bean.getHidden() == 0 ? false : true);
            result.add(sysMenu);
        }
        return result;
    }

    private List<UcSysMenuBean> getSysMenuList(List<Integer> menuIds, Integer hidden, Integer type) throws Exception {
        UcSysMenuBean condition = new UcSysMenuBean();
        condition.setIds(menuIds);
        condition.setHidden(hidden);
        condition.setType(type);
        return userCenterMainDataService.selectUcSysMenu(condition);
    }

    public List<SysRole> queryRolesByUserId(Integer userId) throws Exception {
        UcSysRoleUserBean condition = new UcSysRoleUserBean();
        condition.setUserId(userId);
        List<UcSysRoleUserBean> sysRoleUserList = userCenterMainDataService.selectUcSysRoleUser(condition);

        List<Integer> roleIds = new ArrayList<>();
        if (!CollectionUtils.isEmpty(sysRoleUserList)) {
            for (UcSysRoleUserBean sysRoleUserBean : sysRoleUserList) {
                roleIds.add(sysRoleUserBean.getRoleId());
            }
        }
        List<UcSysRoleBean> sysRoleList = getSysRoleList(roleIds);

        List<SysRole> result = convert(sysRoleList);
        return result;
    }

    private List<UcSysRoleBean> getSysRoleList(List<Integer> roleIds) throws Exception {
        UcSysRoleBean condition = new UcSysRoleBean();
        condition.setIds(roleIds);
        return userCenterMainDataService.selectUcSysRole(condition);
    }

    public List<SysRole> convert(List<UcSysRoleBean> sysRoleList) {
        List<SysRole> result = new ArrayList<>();
        for (UcSysRoleBean bean : sysRoleList) {
            SysRole sysRole = new SysRole();
            BeanUtils.copyProperties(bean, sysRole);
            result.add(sysRole);
        }
        return result;
    }

    @Override
    public SysUser selectByUsername(String username) throws Exception {
        SysUser user = null;
        List<UcSysUserBean> sysUserList = this.getSysUserList(null, username, null);
        if (!CollectionUtils.isEmpty(sysUserList)) {
            UcSysUserBean sysUserBean = sysUserList.get(0);
            user = convert(sysUserBean);
        }
        return user;
    }

    private List<UcSysUserBean> getSysUserList(String mobile, String userName, String openId) throws Exception {
        UcSysUserBean condition = new UcSysUserBean();
        condition.setMobile(mobile);
        condition.setUserName(userName);
        condition.setOpenId(openId);
        return userCenterMainDataService.selectUcSysUser(condition);
    }

    @Override
    public SysUser selectByMobile(String mobile) throws Exception {
        List<UcSysUserBean> sysUserList = this.getSysUserList(mobile, null, null);
        List<SysUser> users = convertUser(sysUserList);
        return getUser(users);
    }

    @Override
    public SysUser selectByOpenId(String openId) throws Exception {
        List<UcSysUserBean> sysUserList = this.getSysUserList(null, null, openId);
        List<SysUser> users = convertUser(sysUserList);
        return getUser(users);
    }

    private SysUser getUser(List<SysUser> users) {
        SysUser user = null;
        if (users != null && !users.isEmpty()) {
            user = users.get(0);
        }
        return user;
    }

    public SysUser convert(UcSysUserBean sysUserBean) throws Exception {
        SysUser sysUser = new SysUser();
        BeanUtils.copyProperties(sysUserBean, sysUser);
        sysUser.setEnabled(sysUserBean.getEnabled() > 0 ? true : false);
        sysUser.setUsername(sysUserBean.getUserName());

        List<SysRole> sysRoles = queryRolesByUserId(sysUser.getId());
        sysUser.setRoles(sysRoles);

        if (!CollectionUtils.isEmpty(sysRoles)) {
            List<Integer> roleIds = sysRoles.parallelStream().map(SysRole::getId).collect(Collectors.toList());
            List<SysMenu> menus = queryMenusByRoleIds(roleIds, SystemConstants.PERMISSION);
            if (!CollectionUtils.isEmpty(menus)) {
                Set<String> permissions = menus.parallelStream().map(p -> p.getPath()).collect(Collectors.toSet());
                sysUser.setPermissions(permissions);
            }
        }
        return sysUser;
    }

    public List<SysUser> convertUser(List<UcSysUserBean> sysUserList) {
        List<SysUser> result = new ArrayList<>();
        for (UcSysUserBean bean : sysUserList) {
            SysUser sysUser = new SysUser();
            BeanUtils.copyProperties(bean, sysUser);
            sysUser.setEnabled(bean.getEnabled() > 0 ? true : false);
            sysUser.setUsername(bean.getUserName());
            result.add(sysUser);
        }
        return result;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void setRoleToUser(Integer id, List<Integer> roleIds) throws Exception {
        SysUser sysUser = getById(id);
        if (sysUser == null) {
            throw new IllegalArgumentException("用户不存在");
        }

        deleteUserRole(id);
        if (!CollectionUtils.isEmpty(roleIds)) {
            for (Integer roleId : roleIds) {
                UcSysRoleUserBean sysRoleUserBean = new UcSysRoleUserBean();
                sysRoleUserBean.setRoleId(roleId);
                sysRoleUserBean.setUserId(id);
                userCenterMainDataService.saveUcSysRoleUser(sysRoleUserBean);
            }
        }
    }

    private void deleteUserRole(Integer userId) throws Exception {
        List<UcSysRoleUserBean> sysRoleUserList = getSysRoleUserList(userId);
        if (!CollectionUtils.isEmpty(sysRoleUserList)) {
            for (UcSysRoleUserBean sysRoleUserBean : sysRoleUserList) {
                userCenterMainDataService.deleteUcSysRoleUser(sysRoleUserBean.getId());
            }
        }
    }

    private List<UcSysRoleUserBean> getSysRoleUserList(Integer userId) throws Exception {
        UcSysRoleUserBean condition = new UcSysRoleUserBean();
        condition.setUserId(userId);
        return userCenterMainDataService.selectUcSysRoleUser(condition);
    }

    @Transactional
    @Override
    public Result updatePassword(Integer id, String oldPassword, String newPassword) throws Exception {
        SysUser sysUser = getById(id);
        if (StrUtil.isNotBlank(oldPassword)) {
            if (!passwordEncoder.matches(oldPassword, sysUser.getPassword())) {
                return Result.failed("旧密码错误");
            }
        }
        if (StrUtil.isBlank(newPassword)) {
            newPassword = SystemConstants.DEF_USER_PASSWORD;
        }
        SysUser user = new SysUser();
        user.setId(id);
        user.setPassword(passwordEncoder.encode(newPassword));
        this.updateById(user);
        return Result.succeed("修改成功");
    }

    @Override
    public PageResult<SysUser> findUsers(Map<String, Object> params) throws Exception {
        Page<SysUser> page = new Page<>(MapUtils.getInteger(params, "page"), MapUtils.getInteger(params, "limit"));

        UcSysUserBean condition = new UcSysUserBean();
        PageParam pageParam = new PageParam();
        pageParam.setPageNo(page.getPageNum());
        pageParam.setPageSize(page.getPageSize());
        DefaultPageResult<UcSysUserBean> pageInfo = userCenterMainDataService.selectUcSysUserByCondition(condition, pageParam);

        page.setTotal(pageInfo.getTotalCount());
        List<UcSysUserBean> sysUserList = pageInfo.getResultList();
        List<SysUser> list = convertUser(sysUserList);

        long total = page.getTotal();
        if (total > 0) {
            List<Integer> userIds = list.stream().map(SysUser::getId).collect(Collectors.toList());
            List<SysRole> sysRoles = new ArrayList<>();
            for (Integer userId : userIds) {
                List<SysRole> sysRoleList = queryRolesByUserId(userId);
                if (!CollectionUtils.isEmpty(sysRoleList)) {
                    sysRoles.addAll(sysRoleList);
                }

            }
            list.forEach(u -> u.setRoles(sysRoles.stream().filter(r -> !ObjectUtils.notEqual(u.getId(), r.getUserId()))
                    .collect(Collectors.toList())));
        }
        return PageResult.<SysUser>builder().data(list).code(0).count(Integer.parseInt(String.valueOf(total))).build();
    }

    @Override
    public List<SysRole> findRolesByUserId(Integer userId) throws Exception {
        return queryRolesByUserId(userId);
    }

    @Override
    public Result updateEnabled(Map<String, Object> params) throws Exception {
        Integer id = MapUtils.getInteger(params, "id");
        Boolean enabled = MapUtils.getBoolean(params, "enabled");

        SysUser appUser = getById(id);
        if (appUser == null) {
            return Result.failed("用户不存在");
        }
        appUser.setEnabled(enabled);
        appUser.setUpdateTime(new Date());

        boolean flag = updateById(appUser);
        log.info("修改用户：{}", appUser);

        return flag ? Result.succeed(appUser, "更新成功") : Result.failed("更新失败");
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Result update(SysUser sysUser) throws Exception {
        if (sysUser.getId() == null) {
            if (StringUtils.isBlank(sysUser.getType())) {
                sysUser.setType(UserType.BACKEND.name());
            }
            sysUser.setPassword(passwordEncoder.encode(SystemConstants.DEF_USER_PASSWORD));
            sysUser.setEnabled(Boolean.TRUE);
        }
        String username = sysUser.getUsername();
        SysUser updateUser = selectByUsername(username);
        if (updateUser == null) {
            throw new Exception("username不能修改");
        }
        updateUser.setSex(sysUser.getSex());
        updateUser.setMobile(sysUser.getMobile());
        updateUser.setEnabled(sysUser.getEnabled());
        updateUser.setType(sysUser.getType());
        updateUser.setPassword(sysUser.getPassword());

        UcSysUserBean sysUserBean = new UcSysUserBean();
        BeanUtils.copyProperties(updateUser, sysUserBean);
        userCenterMainDataService.updateUcSysUser(sysUserBean);

        if (StrUtil.isNotEmpty(sysUser.getRoleId())) {
            deleteUserRole(sysUser.getId());
            List<String> roleIds = Arrays.asList(sysUser.getRoleId().split(","));
            if (!CollectionUtils.isEmpty(roleIds)) {
                for (String roleId : roleIds) {
                    UcSysRoleUserBean sysRoleUserBean = new UcSysRoleUserBean();
                    sysRoleUserBean.setRoleId(Integer.parseInt(roleId));
                    sysRoleUserBean.setUserId(sysUser.getId());
                    userCenterMainDataService.saveUcSysRoleUser(sysRoleUserBean);
                }
            }
        }
        return Result.succeed(sysUser, "操作成功");
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Result add(SysUser sysUser) throws Exception {
        if (StringUtils.isBlank(sysUser.getType())) {
            sysUser.setType(UserType.BACKEND.name());
        }
        sysUser.setPassword(passwordEncoder.encode(SystemConstants.DEF_USER_PASSWORD));
        sysUser.setEnabled(Boolean.TRUE);
        String username = sysUser.getUsername();

        SysUser oldUser = selectByUsername(username);
        if (oldUser != null) {
            throw new Exception("username已存在");
        }

        UcSysUserBean sysUserBean = new UcSysUserBean();
        BeanUtils.copyProperties(sysUser, sysUserBean);
        int i = userCenterMainDataService.saveUcSysUser(sysUserBean);

        boolean result = i > 0 ? true : false;
        if (result && StrUtil.isNotEmpty(sysUser.getRoleId())) {
            deleteUserRole(sysUser.getId());
            List<String> roleIds = Arrays.asList(sysUser.getRoleId().split(","));
            if (!CollectionUtils.isEmpty(roleIds)) {
                for (String roleId : roleIds) {
                    UcSysRoleUserBean sysRoleUserBean = new UcSysRoleUserBean();
                    sysRoleUserBean.setRoleId(Integer.parseInt(roleId));
                    sysRoleUserBean.setUserId(sysUser.getId());
                    userCenterMainDataService.saveUcSysRoleUser(sysRoleUserBean);
                }
            }
        }
        return result ? Result.succeed(sysUser, "操作成功") : Result.failed("操作失败");
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean delUser(Integer id) throws Exception {
        deleteUserRole(id);
        return userCenterMainDataService.deleteUcSysUser(id);
    }

}